/*
 *  Project:            timelyRV_v0.1 -- a RISCV-32I SoC.
 *  Module name:        soc_runtime.
 *  Description:        Top Module of soc_runtime.
 *  Last updated date:  2021.12.03.
 *
 *  Copyright (C) 2021-2022 Junnan Li <lijunnan@nudt.edu.cn>.
 *  Copyright and related rights are licensed under the MIT license.
 *
 *  Noted:
 *    1) rgmii2gmii & gmii_rx2rgmii are processed by language templates;
 *    2) rgmii_rx is constrained by set_input_delay "-2.0 ~ -0.7";
 *    3) 134b pkt data definition: 
 *      [133:132] head tag, 2'b01 is head, 2'b10 is tail;
 *      [131:128] valid tag, 4'b1111 means sixteen 8b data is valid;
 *      [127:0]   pkt data, invalid part is padded with 0;
 *    4) the riscv-32i core is a simplified picoRV32;
 *
 */

`timescale 1ns / 1ps
`define PORT 4

module soc_runtime_mux(
  //* system input, clk;
  input                 clk_125m,
  input                 sys_rst_n,
  //* rgmii port;
  input         [3:0]   rgmii_rd,
  input                 rgmii_rx_ctl,
  input                 rgmii_rxc,
  output  wire  [3:0]   rgmii_td,
  output  wire          rgmii_tx_ctl,
  output  wire          rgmii_txc,
  //* rgmii port 1;
  input         [3:0]   rgmii_rd_1,
  input                 rgmii_rx_ctl_1,
  input                 rgmii_rxc_1,
  output  wire  [3:0]   rgmii_td_1,
  output  wire          rgmii_tx_ctl_1,
  output  wire          rgmii_txc_1,
  //* rgmii port 2;
  input         [3:0]   rgmii_rd_2,
  input                 rgmii_rx_ctl_2,
  input                 rgmii_rxc_2,
  output  wire  [3:0]   rgmii_td_2,
  output  wire          rgmii_tx_ctl_2,
  output  wire          rgmii_txc_2,
  //* rgmii port 3;
  input         [3:0]   rgmii_rd_3,
  input                 rgmii_rx_ctl_3,
  input                 rgmii_rxc_3,
  output  wire  [3:0]   rgmii_td_3,
  output  wire          rgmii_tx_ctl_3,
  output  wire          rgmii_txc_3,
  //* uart rx/tx
  output  wire          pktData_valid_gmii,
  output  wire  [133:0] pktData_gmii,
  output  wire  [15:0]  pkt_length_gmii,
  input   wire          ready_in,
  input                 pktData_valid_um,
  input         [133:0] pktData_um
);

  (* mark_debug = "true"*)reg           [`PORT-1:0]   to_read;
  (* mark_debug = "true"*)reg           [`PORT-1:0]   r_pktData_valid_um;
  (* mark_debug = "true"*)reg           [133:0]       r_pktData_um;
  (* mark_debug = "true"*)wire          [`PORT-1:0]   w_pktData_valid_gmii;
  (* mark_debug = "true"*)wire          [133:0]       w_pktData_gmii[`PORT-1:0];
  (* mark_debug = "true"*)wire          [15:0]        w_pkt_length_gmii[`PORT-1:0];
  (* mark_debug = "true"*)reg           [3:0]         state_mux, state_dmux;
  localparam            IDLE_S    = 4'd0,
                        WAIT_0_S  = 4'd1,
                        WAIT_1_S  = 4'd2,
                        WAIT_2_S  = 4'd3,
                        WAIT_3_S  = 4'd4;

  //======================= port_0      =====================//
  //* rgmii <==> 134b pkt (without metadata);
  soc_runtime runtime_0(
    .clk_125m             (clk_125m                     ),
    .sys_rst_n            (sys_rst_n                    ),
    //* rgmii input;
    .rgmii_rd             (rgmii_rd                     ),  //* input
    .rgmii_rx_ctl         (rgmii_rx_ctl                 ),  //* input
    .rgmii_rxc            (rgmii_rxc                    ),  //* input
    //* rgmii output;
    .rgmii_txc            (rgmii_txc                    ),  //* output
    .rgmii_td             (rgmii_td                     ),  //* output
    .rgmii_tx_ctl         (rgmii_tx_ctl                 ),  //* output
    //* um;
    .pktData_valid_gmii   (w_pktData_valid_gmii[0]      ),
    .pktData_gmii         (w_pktData_gmii[0]            ),
    .pkt_length_gmii      (w_pkt_length_gmii[0]         ),
    .ready_in             (to_read[0]                   ),
    // .ready_in             (1'b1                         ),
    .pktData_valid_um     (r_pktData_valid_um[0]        ),
    .pktData_um           (r_pktData_um                 )
  );

  //======================= port_1      =====================//
  //* rgmii <==> 134b pkt (without metadata);
  soc_runtime runtime_1(
    .clk_125m             (clk_125m                     ),
    .sys_rst_n            (sys_rst_n                    ),
    //* rgmii input;
    .rgmii_rd             (rgmii_rd_1                   ),  //* input
    .rgmii_rx_ctl         (rgmii_rx_ctl_1               ),  //* input
    .rgmii_rxc            (rgmii_rxc_1                  ),  //* input
    //* rgmii output;
    .rgmii_txc            (rgmii_txc_1                  ),  //* output
    .rgmii_td             (rgmii_td_1                   ),  //* output
    .rgmii_tx_ctl         (rgmii_tx_ctl_1               ),  //* output
    //* um;
    .pktData_valid_gmii   (w_pktData_valid_gmii[1]      ),
    .pktData_gmii         (w_pktData_gmii[1]            ),
    .pkt_length_gmii      (w_pkt_length_gmii[1]         ),
    .ready_in             (to_read[1]                   ),
    // .ready_in             (1'b1                         ),
    .pktData_valid_um     (r_pktData_valid_um[1]        ),
    .pktData_um           (r_pktData_um                 )
  );

  //======================= port_2      =====================//
  //* rgmii <==> 134b pkt (without metadata);
  soc_runtime runtime_2(
    .clk_125m             (clk_125m                     ),
    .sys_rst_n            (sys_rst_n                    ),
    //* rgmii input;
    .rgmii_rd             (rgmii_rd_2                   ),  //* input
    .rgmii_rx_ctl         (rgmii_rx_ctl_2               ),  //* input
    .rgmii_rxc            (rgmii_rxc_2                  ),  //* input
    //* rgmii output;
    .rgmii_txc            (rgmii_txc_2                  ),  //* output
    .rgmii_td             (rgmii_td_2                   ),  //* output
    .rgmii_tx_ctl         (rgmii_tx_ctl_2               ),  //* output
    //* um;
    .pktData_valid_gmii   (w_pktData_valid_gmii[2]      ),
    .pktData_gmii         (w_pktData_gmii[2]            ),
    .pkt_length_gmii      (w_pkt_length_gmii[2]         ),
    .ready_in             (to_read[2]                   ),
    // .ready_in             (1'b1                         ),
    .pktData_valid_um     (r_pktData_valid_um[2]        ),
    .pktData_um           (r_pktData_um                 )
  );

  //======================= port_3      =====================//
  //* rgmii <==> 134b pkt (without metadata);
  soc_runtime runtime_3(
    .clk_125m             (clk_125m                     ),
    .sys_rst_n            (sys_rst_n                    ),
    //* rgmii input;
    .rgmii_rd             (rgmii_rd_3                   ),  //* input
    .rgmii_rx_ctl         (rgmii_rx_ctl_3               ),  //* input
    .rgmii_rxc            (rgmii_rxc_3                  ),  //* input
    //* rgmii output;
    .rgmii_txc            (rgmii_txc_3                  ),  //* output
    .rgmii_td             (rgmii_td_3                   ),  //* output
    .rgmii_tx_ctl         (rgmii_tx_ctl_3               ),  //* output
    //* um;
    .pktData_valid_gmii   (w_pktData_valid_gmii[3]      ),
    .pktData_gmii         (w_pktData_gmii[3]            ),
    .pkt_length_gmii      (w_pkt_length_gmii[3]         ),
    .ready_in             (to_read[3]                   ),
    // .ready_in             (1'b1                         ),
    .pktData_valid_um     (r_pktData_valid_um[3]        ),
    .pktData_um           (r_pktData_um                 )
  );

  reg [`PORT-1:0]     tag;
  always @(posedge clk_125m or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
      to_read                 <= {`PORT{1'b0}};
      tag                     <= {`PORT{1'b0}};
      tag[0]                  <= 1'b1;
      state_mux               <= IDLE_S;
    end
    else begin
      case(state_mux)
        IDLE_S: begin
          to_read             <= tag;
          tag                 <= {tag[2:0], tag[3]};
          state_mux           <= WAIT_0_S;
        end
        WAIT_0_S: begin
          to_read             <= {`PORT{1'b0}};
          state_mux           <= WAIT_1_S;
        end
        WAIT_1_S: begin
          state_mux           <= WAIT_2_S;
        end
        WAIT_2_S: begin
          state_mux           <= WAIT_3_S;
        end
        WAIT_3_S: begin
          if(|w_pktData_valid_gmii == 1'b0)
            state_mux         <= IDLE_S;
          else
            state_mux         <= WAIT_3_S;
        end
        default: begin
          state_mux           <= IDLE_S;
        end
      endcase
    end
  end

  assign pktData_valid_gmii = |w_pktData_valid_gmii;
  assign pktData_gmii       = (w_pktData_valid_gmii[0] && 
          w_pktData_gmii[0][133:132] == 2'b01)? 
            {2'b01,4'd0,w_pktData_gmii[0][127:0]}: 
          (w_pktData_valid_gmii[0])? w_pktData_gmii[0]: 
          (w_pktData_valid_gmii[1] && w_pktData_gmii[1][133:132] == 2'b01)? 
            {2'b01,4'd1,w_pktData_gmii[1][127:0]}: 
            // (w_pktData_valid_gmii[1])? w_pktData_gmii[1]: 134'b0;
          (w_pktData_valid_gmii[1])? w_pktData_gmii[1]: 
          (w_pktData_valid_gmii[2] && w_pktData_gmii[2][133:132] == 2'b01)? 
            {2'b01,4'd2,w_pktData_gmii[2][127:0]}: 
          (w_pktData_valid_gmii[2])? w_pktData_gmii[2]: 
          (w_pktData_valid_gmii[3] && w_pktData_gmii[3][133:132] == 2'b01)? 
            {2'b01,4'd3,w_pktData_gmii[3][127:0]}: 
          (w_pktData_valid_gmii[3])? w_pktData_gmii[3]: 134'b0;
  assign pkt_length_gmii    = (w_pktData_valid_gmii[0] && 
          w_pktData_gmii[0][133:132] == 2'b01)? w_pkt_length_gmii[0]:
          (w_pktData_valid_gmii[1] && w_pktData_gmii[1][133:132] == 2'b01)? w_pkt_length_gmii[1]: 
          (w_pktData_valid_gmii[2] && w_pktData_gmii[2][133:132] == 2'b01)? w_pkt_length_gmii[2]:
                                                                            w_pkt_length_gmii[3];

  always @(posedge clk_125m or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
      r_pktData_valid_um      <= {`PORT{1'b0}};
      r_pktData_um            <= 134'b0;
      state_dmux              <= IDLE_S;
    end
    else begin
      r_pktData_um            <= pktData_um;
      case(state_dmux)
        IDLE_S: begin
          r_pktData_valid_um  <= {`PORT{1'b0}};
          state_dmux          <= IDLE_S;
          if(pktData_valid_um == 1'b1 && pktData_um[133:132] == 2'b01) begin
            r_pktData_valid_um<= pktData_um[128+:`PORT];
            r_pktData_um      <= {2'b01,4'hf,pktData_um[127:0]};
            state_dmux        <= WAIT_0_S;
          end
        end
        WAIT_0_S: begin
          if(pktData_um[133:132] == 2'b10)
            state_dmux        <= IDLE_S;
          else
            state_dmux        <= WAIT_0_S;
        end
        default: begin
          state_dmux          <= IDLE_S;
        end
      endcase
    end
  end

endmodule
